﻿/*
  Copyright (c) 2011, Mikheev Rostislav
  Copyright (c) 2011, FreshFarsh team

  Steel Engine source code is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
  @author Mikheev Rostislav <hacenator@gmail.com>
  @date 23.03.2011
  @brief Extended math functions and macroses
*/

#ifndef _H_STEEL_MATH
#define _H_STEEL_MATH

#include <math.h>
#include "structures.h"

namespace steel {

// Point2 Operations Macroses
#define Point2Distance(a, b) 	( sqrt(pow((b.x - a.x), 2) + pow((b.y - a.y), 2)) )
#define Point2Equal(a, b) 		( a.x == b.x && a.y == b.y )
#define Point2Mul(a, b) 		( a.x * b.x + a.y * b.y )
#define Point2Sub(a, b, c)  	{ c.x = a.x - b.x; c.y = a.y - b.y; }
#define Point2Add(a, b, c)	    { c.x = a.x + b.x; c.y = a.y + b.y; }
#define	Point2Scale(a, b, c)	{ c.x = a.x * b; c.y = a.y * b; }
#define Point2Clear(a)		    { a.x = a.y = 0; }
#define Point2Inverse(a, b)	    { b.x = -a.x; b.y = -a.y; }

/* :)
#define Point3Mul(a, b) 		( a.x * b.x + a.y * b.y + a.z * b.z )
#define Point3Sub(a, b, c)  	{ c.x = a.x - b.x; c.y = a.y - b.y; c.z = a.z - b.z; }
#define Point3Add(a, b, c)	    { c.x = a.x + b.x; c.y = a.y + b.y; c.z = a.z + b.z; }
#define	Point3Scale(a, b, c)	{ c.x = a.x * b; c.y = a.y * b; c.z = a.z * b; }
#define Point3Clear(a)		    { a.x = a.y = a.z = 0; }
#define Point3Inverse(a, b)	    { b.x = -a.x; b.y = -a.y; b.z = -a.z; }
*/

/**
  Returns the number in degree of two and greater than x
  @param x
    Input number
  @return
    number in degree of two and greater than x
*/
inline int NextPowerOfTwo(int x);

/**
  Calculate 2D polynomial based on array of 4 points
  @param u
    interploation value between 0 and 1
  @return
    Point to calculate. 2 vertex points and 2 intermediate points
*/
Point2f Bernstein(float u, Point2f *p);

/**
 * Find the intersetcion of two segments
 * @param a1
 * 	Segment A first point
 * @param a2
 * 	Segment A second point
 * @param b1
 *	Segment B first point
 * @param b2
 * 	Segment B second point
 * @param c
 * 	Intersection point
 * @return
 * 	"true" if intersection is found, or "false" if segments has not 
 * intersections (or parallel)
 */
bool SegmentsIntersection(const Point2f a1, const Point2f a2, 
  const Point2f b1, const Point2f b2, Point2f c);
	
/**
  Check collision between two circles
  @param p1
    First circle coordinate
  @param radius1
    First circle radius
  @param p2
    Second circle coordinate
  @param radius2
    Second circle radius
*/
bool CollisionCircleWithCircle(const Point2f p1, 
	const float radius1, const Point2f p2, const float radius2);

/*****************************************************
                        Matrix4x4
******************************************************/
/**
    Matrix4x4 Mul
    @param a
        First matrix
    @param b
        Secont matrix
    @param r
        Result
*/
#define Matrix4fMul(a, b, r) {  \
	    r[0*4+0] = a[0*4+0]*b[0*4+0] + a[0*4+1]*b[1*4+0] + a[0*4+2]*b[2*4+0] + a[0*4+3]*b[3*4+0]; \
	    r[0*4+1] = a[0*4+0]*b[0*4+1] + a[0*4+1]*b[1*4+1] + a[0*4+2]*b[2*4+1] + a[0*4+3]*b[3*4+1]; \
	    r[0*4+2] = a[0*4+0]*b[0*4+2] + a[0*4+1]*b[1*4+2] + a[0*4+2]*b[2*4+2] + a[0*4+3]*b[3*4+2]; \
	    r[0*4+3] = a[0*4+0]*b[0*4+3] + a[0*4+1]*b[1*4+3] + a[0*4+2]*b[2*4+3] + a[0*4+3]*b[3*4+3]; \
        \
	    r[1*4+0] = a[1*4+0]*b[0*4+0] + a[1*4+1]*b[1*4+0] + a[1*4+2]*b[2*4+0] + a[1*4+3]*b[3*4+0]; \
	    r[1*4+1] = a[1*4+0]*b[0*4+1] + a[1*4+1]*b[1*4+1] + a[1*4+2]*b[2*4+1] + a[1*4+3]*b[3*4+1]; \
	    r[1*4+2] = a[1*4+0]*b[0*4+2] + a[1*4+1]*b[1*4+2] + a[1*4+2]*b[2*4+2] + a[1*4+3]*b[3*4+2]; \
	    r[1*4+3] = a[1*4+0]*b[0*4+3] + a[1*4+1]*b[1*4+3] + a[1*4+2]*b[2*4+3] + a[1*4+3]*b[3*4+3]; \
        \
	    r[2*4+0] = a[2*4+0]*b[0*4+0] + a[2*4+1]*b[1*4+0] + a[2*4+2]*b[2*4+0] + a[2*4+3]*b[3*4+0]; \
	    r[2*4+1] = a[2*4+0]*b[0*4+1] + a[2*4+1]*b[1*4+1] + a[2*4+2]*b[2*4+1] + a[2*4+3]*b[3*4+1]; \
	    r[2*4+2] = a[2*4+0]*b[0*4+2] + a[2*4+1]*b[1*4+2] + a[2*4+2]*b[2*4+2] + a[2*4+3]*b[3*4+2]; \
	    r[2*4+3] = a[2*4+0]*b[0*4+3] + a[2*4+1]*b[1*4+3] + a[2*4+2]*b[2*4+3] + a[2*4+3]*b[3*4+3]; \
        \
	    r[3*4+0] = a[3*4+0]*b[0*4+0] + a[3*4+1]*b[1*4+0] + a[3*4+2]*b[2*4+0] + a[3*4+3]*b[3*4+0]; \
	    r[3*4+1] = a[3*4+0]*b[0*4+1] + a[3*4+1]*b[1*4+1] + a[3*4+2]*b[2*4+1] + a[3*4+3]*b[3*4+1]; \
	    r[3*4+2] = a[3*4+0]*b[0*4+2] + a[3*4+1]*b[1*4+2] + a[3*4+2]*b[2*4+2] + a[3*4+3]*b[3*4+2]; \
	    r[3*4+3] = a[3*4+0]*b[0*4+3] + a[3*4+1]*b[1*4+3] + a[3*4+2]*b[2*4+3] + a[3*4+3]*b[3*4+3]; \
}


/**
    Invert matrix
*/
/*bool Matrix4fInvert(const jMatrix4x4f in, jMatrix4x4f out) {
    jMatrix4x4f inverted;
    float det;

    int i;

    #define k(a1,a2,a3,a4,a5,a6,a7,a8,a9,) inverted.array[0] = m.array[a1]*m.array[a2]*m.array[a3] \
        - m.array[a1]*m.array[a4]*m.array[a5] \
        - m.array[a6]*m.array[a7]*m.array[a3] \
        + m.array[a6]*m.array[a8]*m.array[a5] \
        + m.array[a9]*m.array[a7]*m.array[a4] \
        - m.array[a9]*m.array[a8]*m.array[a2];

    inverted.array[0] = m.array[5]*m.array[10]*m.array[15]
        - m.array[5]*m.array[11]*m.array[14]
        - m.array[9]*m.array[6]*m.array[15]
        + m.array[9]*m.array[7]*m.array[14]
        + m.array[13]*m.array[6]*m.array[11]
        - m.array[13]*m.array[7]*m.array[10];
    inverted.array[4] =  -m.array[4]*m.array[10]*m.array[15]
        + m.array[4]*m.array[11]*m.array[14]
        + m.array[8]*m.array[6]*m.array[15]
        - m.array[8]*m.array[7]*m.array[14]
        - m.array[12]*m.array[6]*m.array[11]
        + m.array[12]*m.array[7]*m.array[10];
    inverted.array[8] = m.array[4]*m.array[9]*m.array[15]
        - m.array[4]*m.array[11]*m.array[13]
        - m.array[8]*m.array[5]*m.array[15]
        + m.array[8]*m.array[7]*m.array[13]
        + m.array[12]*m.array[5]*m.array[11]
        - m.array[12]*m.array[7]*m.array[9];
    inverted.array[12] = -m.array[4]*m.array[9]*m.array[14]
        + m.array[4]*m.array[10]*m.array[13]
        + m.array[8]*m.array[5]*m.array[14]
        - m.array[8]*m.array[6]*m.array[13]
        - m.array[12]*m.array[5]*m.array[10]
        + m.array[12]*m.array[6]*m.array[9];
    inverted.array[1] =  -m.array[1]*m.array[10]*m.array[15]
        + m.array[1]*m.array[11]*m.array[14]
        + m.array[9]*m.array[2]*m.array[15]
        - m.array[9]*m.array[3]*m.array[14]
        - m.array[13]*m.array[2]*m.array[11]
        + m.array[13]*m.array[3]*m.array[10];
    inverted.array[5] =   m.array[0]*m.array[10]*m.array[15]
        - m.array[0]*m.array[11]*m.array[14]
        - m.array[8]*m.array[2]*m.array[15]
        + m.array[8]*m.array[3]*m.array[14]
        + m.array[12]*m.array[2]*m.array[11]
        - m.array[12]*m.array[3]*m.array[10];
    inverted.array[9] =  -m.array[0]*m.array[9]*m.array[15]
        + m.array[0]*m.array[11]*m.array[13]
        + m.array[8]*m.array[1]*m.array[15]
        - m.array[8]*m.array[3]*m.array[13]
        - m.array[12]*m.array[1]*m.array[11]
        + m.array[12]*m.array[3]*m.array[9];
    inverted.array[13] =  m.array[0]*m.array[9]*m.array[14]
        - m.array[0]*m.array[10]*m.array[13]
        - m.array[8]*m.array[1]*m.array[14]
        + m.array[8]*m.array[2]*m.array[13]
        + m.array[12]*m.array[1]*m.array[10]
        - m.array[12]*m.array[2]*m.array[9];
    inverted.array[2] =   m.array[1]*m.array[6]*m.array[15]
        - m.array[1]*m.array[7]*m.array[14]
        - m.array[5]*m.array[2]*m.array[15]
        + m.array[5]*m.array[3]*m.array[14]
        + m.array[13]*m.array[2]*m.array[7]
        - m.array[13]*m.array[3]*m.array[6];
    inverted.array[6] =  -m.array[0]*m.array[6]*m.array[15]
        + m.array[0]*m.array[7]*m.array[14]
        + m.array[4]*m.array[2]*m.array[15]
        - m.array[4]*m.array[3]*m.array[14]
        - m.array[12]*m.array[2]*m.array[7]
        + m.array[12]*m.array[3]*m.array[6];
    inverted.array[10] =  m.array[0]*m.array[5]*m.array[15]
        - m.array[0]*m.array[7]*m.array[13]
        - m.array[4]*m.array[1]*m.array[15]
        + m.array[4]*m.array[3]*m.array[13]
        + m.array[12]*m.array[1]*m.array[7]
        - m.array[12]*m.array[3]*m.array[5];
    inverted.array[14] = -m.array[0]*m.array[5]*m.array[14]
        + m.array[0]*m.array[6]*m.array[13]
        + m.array[4]*m.array[1]*m.array[14]
        - m.array[4]*m.array[2]*m.array[13]
        - m.array[12]*m.array[1]*m.array[6]
        + m.array[12]*m.array[2]*m.array[5];
    inverted.array[3] =  -m.array[1]*m.array[6]*m.array[11]
        + m.array[1]*m.array[7]*m.array[10]
        + m.array[5]*m.array[2]*m.array[11]
        - m.array[5]*m.array[3]*m.array[10]
        - m.array[9]*m.array[2]*m.array[7]
        + m.array[9]*m.array[3]*m.array[6];
    inverted.array[7] =   m.array[0]*m.array[6]*m.array[11]
        - m.array[0]*m.array[7]*m.array[10]
        - m.array[4]*m.array[2]*m.array[11]
        + m.array[4]*m.array[3]*m.array[10]
        + m.array[8]*m.array[2]*m.array[7]
        - m.array[8]*m.array[3]*m.array[6];
    inverted.array[11] = -m.array[0]*m.array[5]*m.array[11]
        + m.array[0]*m.array[7]*m.array[9]
        + m.array[4]*m.array[1]*m.array[11]
        - m.array[4]*m.array[3]*m.array[9]
        - m.array[8]*m.array[1]*m.array[7]
        + m.array[8]*m.array[3]*m.array[5];
    inverted.array[15] =  m.array[0]*m.array[5]*m.array[10]
        - m.array[0]*m.array[6]*m.array[9]
        - m.array[4]*m.array[1]*m.array[10]
        + m.array[4]*m.array[2]*m.array[9]
        + m.array[8]*m.array[1]*m.array[6]
        - m.array[8]*m.array[2]*m.array[5];

    det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12];
    if (det == 0)
        return false;

    det = 1.0 / det;

    for (i = 0; i < 16; i++)
        invOut[i] = inv[i] * det;

    return true;
}*/

}

#endif // _H_STEEL_MATH
